Skip to content

Conversation

ldionne
Copy link
Member

@ldionne ldionne commented Jul 7, 2025

In rare circumstances, the invariants could fail to be restored.

In rare circumstances, the invariants could fail to be restored.
@ldionne ldionne requested a review from a team as a code owner July 7, 2025 20:28
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 7, 2025

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

Changes

In rare circumstances, the invariants could fail to be restored.


Full diff: https://github.com/llvm/llvm-project/pull/147389.diff

1 Files Affected:

  • (modified) libcxx/include/fstream (+24-11)
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index c86f709bedb80..283e31294cc73 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -821,6 +821,14 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
 
 template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::overflow(int_type __c) {
+  auto __failure = [this] {
+    if (this->pptr() == this->epptr() + 1) {
+      this->pbump(-1); // lose the character we overflowed above -- we don't really have a
+                       // choice since we couldn't commit the contents of the put area
+    }
+    return traits_type::eof();
+  };
+
   if (__file_ == nullptr)
     return traits_type::eof();
   __write_mode();
@@ -841,8 +849,9 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
 
   if (__always_noconv_) {
     size_t __n = static_cast<size_t>(this->pptr() - this->pbase());
-    if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n)
-      return traits_type::eof();
+    if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n) {
+      return __failure();
+    }
   } else {
     if (!__cv_)
       std::__throw_bad_cast();
@@ -854,34 +863,38 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
     char* __extbuf_end = __extbuf_;
     do {
       codecvt_base::result __r = __cv_->out(__st_, __b, __p, __end, __extbuf_, __extbuf_ + __ebs_, __extbuf_end);
-      if (__end == __b)
-        return traits_type::eof();
+      if (__end == __b) {
+        return __failure();
+      }
 
       // No conversion needed: output characters directly to the file, done.
       if (__r == codecvt_base::noconv) {
         size_t __n = static_cast<size_t>(__p - __b);
-        if (std::fwrite(__b, 1, __n, __file_) != __n)
-          return traits_type::eof();
+        if (std::fwrite(__b, 1, __n, __file_) != __n) {
+          return __failure();
+        }
         break;
 
         // Conversion successful: output the converted characters to the file, done.
       } else if (__r == codecvt_base::ok) {
         size_t __n = static_cast<size_t>(__extbuf_end - __extbuf_);
-        if (std::fwrite(__extbuf_, 1, __n, __file_) != __n)
-          return traits_type::eof();
+        if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) {
+          return __failure();
+        }
         break;
 
         // Conversion partially successful: output converted characters to the file and repeat with the
         // remaining characters.
       } else if (__r == codecvt_base::partial) {
         size_t __n = static_cast<size_t>(__extbuf_end - __extbuf_);
-        if (std::fwrite(__extbuf_, 1, __n, __file_) != __n)
-          return traits_type::eof();
+        if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) {
+          return __failure();
+        }
         __b = const_cast<char_type*>(__end);
         continue;
 
       } else {
-        return traits_type::eof();
+        return __failure();
       }
     } while (true);
   }

@@ -841,8 +849,9 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>

if (__always_noconv_) {
size_t __n = static_cast<size_t>(this->pptr() - this->pbase());
if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n)
return traits_type::eof();
if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC you're currently only testing this path. We should also test the fwrite calls in the else path (i.e. have a locale that's not __always_noconv_ and have that return the different possible states).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The facet for wchar_t returns always_noconv() == false, so we're also testing below. We could try to hit every branch in the if but I think there is diminishing returns.

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with merging this as-is into main, but I do think we should look into improving our test coverage (in general). AFAICT we have not even a single test that actually tests all the branches.

@ldionne ldionne merged commit 6291b63 into llvm:main Jul 15, 2025
74 of 77 checks passed
@ldionne ldionne deleted the review/fix-basic_filebuf branch July 15, 2025 14:40
@ldionne ldionne added this to the LLVM 21.x Release milestone Jul 15, 2025
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status Jul 15, 2025
@ldionne
Copy link
Member Author

ldionne commented Jul 15, 2025

/cherry-pick 6291b63

@tru tru moved this from Needs Triage to Done in LLVM Release Status Jul 16, 2025
Michael137 added a commit that referenced this pull request Jul 17, 2025
…149390)

This patch makes the `__failed` lambda a member function on `fstream`.
This fixes two LLDB expression evaluation test failures that got
introduced with #147389:
```
16:22:51  ********************
16:22:51  Unresolved Tests (2):
16:22:51    lldb-api :: commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
16:22:51    lldb-api :: commands/expression/import-std-module/list/TestListFromStdModule.py
```

The expression evaluator is asserting in the Clang parser:
```
Assertion failed: (capture_size() == Class->capture_size() && "Wrong number of captures"), function LambdaExpr, file ExprCXX.cpp, line 1277.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
```

Ideally we'd figure out why LLDB is falling over on this lambda. But to
unblock CI for now, make this a member function.

In the long run we should figure out the LLDB bug here so libc++ doesn't
need to care about whether it uses lambdas like this or not.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jul 17, 2025
…r function (#149390)

This patch makes the `__failed` lambda a member function on `fstream`.
This fixes two LLDB expression evaluation test failures that got
introduced with llvm/llvm-project#147389:
```
16:22:51  ********************
16:22:51  Unresolved Tests (2):
16:22:51    lldb-api :: commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
16:22:51    lldb-api :: commands/expression/import-std-module/list/TestListFromStdModule.py
```

The expression evaluator is asserting in the Clang parser:
```
Assertion failed: (capture_size() == Class->capture_size() && "Wrong number of captures"), function LambdaExpr, file ExprCXX.cpp, line 1277.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
```

Ideally we'd figure out why LLDB is falling over on this lambda. But to
unblock CI for now, make this a member function.

In the long run we should figure out the LLDB bug here so libc++ doesn't
need to care about whether it uses lambdas like this or not.
@var-const
Copy link
Member

/cherry-pick 6291b63

@llvmbot
Copy link
Member

llvmbot commented Aug 27, 2025

/pull-request #155712

@tuliom
Copy link
Contributor

tuliom commented Aug 29, 2025

/cherry-pick 6291b63 8f4deff 0bbb794

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
Development

Successfully merging this pull request may close these issues.

5 participants